﻿using System;
using System.ComponentModel;
using System.Data;

namespace Library.OrmEnterpriseEntity.TableInfo {
	///<summary>
	///</summary>
	public abstract class DbColumn : IColumn, ISupportInitialize {

		protected DbColumnView ColumnMetadata { get; private set; }
		private bool _isInit;
		protected bool IsInitialize {
			get {
				if (Table.IsInitialize)
					return true;
				return _isInit;
			}
			private set { _isInit = value; }
		}
		protected DbColumn(DbColumnView metadata, DbTable table) {
			ColumnMetadata = metadata;
			BeginInit();
			Table = table;

			table.Columns.Add(this);
			ColumnIndex = table.Columns.Count - 1;
			if (ColumnIndex != metadata.ColumnIndex) {
				throw new Exception(String.Format("ColumnIndex {0}:{1} is not the same metadata index",
					metadata.ColumnName,
					metadata.TableView.TableName));
			}
			EndInit();
		}

		protected abstract object DefaultValue { get; }

		///<summary>
		/// Keep DB value
		///</summary>
		public object Original { get; internal set; }
		/// <summary>
		/// Current value
		/// </summary>
		public abstract object DbValue { get; set; }

		/// <summary>
		/// Type of value
		/// </summary>
		public abstract Type PropertyType { get; }
		/// <summary>
		/// Column name in DB
		/// </summary>


		public virtual string ColumnName { get { return ColumnMetadata.ColumnName; } }
		/// <summary>
		/// Write value to Db value
		/// </summary>
		/// <param name="value"></param>
		public void WriteFromDb(object value) {
			if (Equals(DBNull.Value, value) || value == null) {
				DbValue = DefaultValue;
				Original = DefaultValue;
			} else {
				DbValue = value;
				Original = value;
			}
		}
		internal virtual void IntWriteFromDb(object value) {
			WriteFromDb(value);
		}
		/// <summary>
		/// Check current value is different from Db value
		/// </summary>
		public virtual bool ValueDbChange {
			get {
				if (Original == null) {
					return ValueNotDefault;
				}
				return !Original.Equals(DbValue);
			}
		}

		/// <summary>
		/// DbType of value
		/// </summary>
		public DbType DbType { get { return ColumnMetadata.DbType; } }
		/// <summary>
		/// Table of this
		/// </summary>
		public DbTable Table { get; private set; }
		/// <summary>
		/// Check if column is Key. true if columm is RowId, otherwise false
		/// </summary>
		public virtual bool IsKey { get { return ColumnMetadata.IsKey; } }

		public DbTableView TableView {
			get { return ColumnMetadata.TableView; }
		}

		///<summary>
		///</summary>
		public DbColumnView ColumnView {
			get { return ColumnMetadata; }
		}

		/// <summary>
		/// Force column has to get value before update/insert to DB
		/// </summary>
		public virtual bool ForceHasValue { get { return ColumnMetadata.ForceHasValue; } }
		/// <summary>
		/// Column is unique per row type
		/// </summary>
		public virtual bool Unique { get { return ColumnMetadata.Unique; } }

		/// <summary>
		/// 
		/// </summary>
		/// <exception cref="ValidattionException"></exception>
		/// <returns></returns>
		public virtual bool Validate() {
			if (!ValueNotDefault && ForceHasValue) {
				var e = new ThrowValidationExceptionEventArgs(this, ValidationError.MissingValue);
				Table.OnThrowValidation(e);
				var error = e.Message;
				if (String.IsNullOrEmpty(error)) error = Factor.DbContext.Instance.Context.StringResource.MissingColumnValue(this, e.ColumnName, e.ColumnValue, e.TableName);
				throw new NotAssignValueException(error, this);
			}

			return true;
		}
		/// <summary>
		/// Current column index in Entity, the in index is application index only, not the same column index in DB
		/// </summary>
		public int ColumnIndex { get; private set; }

		//public QueryParameter CreateParameter(object value) {
		//  return new QueryParameter(ColumnName, DbType, value);
		//}

		internal void Clone(DbColumn column) {
			//column.IsKey = IsKey;
			column.Original = Original;
			column.DbValue = DbValue;
		}

		public override string ToString() {
			return DbValue.ToString();
		}

		internal virtual bool ValueNotDefault {
			get { return false; }
		}

		private object _bkDbValue;
		private object _bkOriginal;
		private bool _isCallBackup;
		/// <summary>
		/// backup DbValue and Original
		/// </summary>
		public void BackupValue() {
			_bkDbValue = DbValue;
			_bkOriginal = Original;
			_isCallBackup = true;
		}
		/// <summary>
		/// Restore value of DbValue and Original from backup
		/// </summary>
		public void RestoreValue() {
			if (!_isCallBackup) {
				return;
				//throw new TableInfoException("Call BackupValue method first");
			}
			DbValue = _bkDbValue;
			Original = _bkOriginal;
		}
		/// <summary>
		/// Clear backup value , prevent call RestoreValue without call BackupValue
		/// </summary>
		public void ClearBackup() {
			_isCallBackup = false;
			_bkOriginal = null;
			_bkDbValue = null;
		}

		public void BeginInit() {
			IsInitialize = true;
		}

		public void EndInit() {
			IsInitialize = false;
		}

	}
}
